package com.bay1ts.security;

import com.bay1ts.dao.UsersMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.Assert;

public class DynamicPasswordAuthenticationProvider extends DaoAuthenticationProvider{
	@Autowired
	private UsersMapper usersMapper;
	
	
	//这里的结构都乱了。整体实现后记着来重写一下。

	@Override
	public boolean supports(java.lang.Class<? extends Object> authentication) {
		return(DynamicloginToken.class.isAssignableFrom(authentication));
	};
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
				messages.getMessage(
						"AbstractUserDetailsAuthenticationProvider.onlySupports",
						"Only UsernamePasswordAuthenticationToken is supported"));

		// Determine username
		String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
				: authentication.getName();

		boolean cacheWasUsed = true;
		//请注意啦父老乡亲们，因为没有用缓存，所以这里是没东西的，注定会用下面那个
		UserDetails user = this.getUserCache().getUserFromCache(username);

		if (user == null) {
			cacheWasUsed = false;

			try {
				//接上句，因为我们的phonenumber和code没有在users表里面，so，这里不要用userdetails，起码要用waper（也要及时修改。。。）
				//请注意啦，这里是从数据库里拿出来的，密码也是。
				//retrieveUser方法会从数据库里拿出user的信息。
				//这个代码块是肯定得重写的。主要是下面这个方法。
//				user = retrieveUser(username,
//						(UsernamePasswordAuthenticationToken) authentication);
				//下面是函数里的代码
				//抽取一下上面那个函数，重写一下。
				UserDetails loadedUser;

				try {
					//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
					//请注意这里有逻辑，如果没有，咋办。try里哪抛异常了？？其实是有的，，，看实现。
					


					//这里逻辑有问题！实际上以为是用户名，其实是code。
//					System.out.println("期待的验证码是"+authentication.getCredentials().toString());
					//在filter里面查了一下，，应该是表单弄错了
					loadedUser = getUserDetailsService().loadUserByUsername(usersMapper.getUsernameByPhonenumber(username));
				}
				catch (UsernameNotFoundException notFound) {
					//没有这个手机号
					if (authentication.getCredentials() != null) {
						String presentedPassword = authentication.getCredentials().toString();

					}
					throw notFound;
				}
				catch (Exception repositoryProblem) {
					throw new InternalAuthenticationServiceException(
							repositoryProblem.getMessage(), repositoryProblem);
				}

				if (loadedUser == null) {
					throw new InternalAuthenticationServiceException(
							"UserDetailsService returned null, which is an interface contract violation");
				}
				user= loadedUser;
				//上面是函数里的代码
			}
			catch (UsernameNotFoundException notFound) {
				logger.debug("User '" + username + "' not found");

				if (hideUserNotFoundExceptions) {
					throw new BadCredentialsException(messages.getMessage(
							"AbstractUserDetailsAuthenticationProvider.badCredentials",
							"Bad credentials"));
				}
				else {
					throw notFound;
				}
			}

			Assert.notNull(user,
					"retrieveUser returned null - a violation of the interface contract");
		}

		try {
			//可以检查一下，不过这里人家本来要用的是username做这些操作，你用的是phonenumber，让人去查吧。省的成一个漏洞。本来被禁用了，你一动态登录就又能用(因为没检查是否禁用了)了。
			//下面一句跟进去，看94行，在比对密码，所以这里最好是实现一下，改了改成验证code，或者直接把逻辑写在下面。
			//userdetails里面的getpassword得到的是数据库里面的。大概知道该重写哪了。retrieveUser和下面这个函数
			//retrieveUser方法会从数据库里拿出user的信息。additionalAuthenticationChecks会验证一下是不是一样
			//验证的问题就解决了，剩下的就是怎么让系统知道phonenumber代表的那个username验证成功了。。
			//这个方法没问题
			getPreAuthenticationChecks().check(user);
			//这个方法有问题。
//			additionalAuthenticationChecks(user,
//					(UsernamePasswordAuthenticationToken) authentication);
			//下面是上面方法的主要部分
			if (authentication.getCredentials() == null) {
				logger.debug("Authentication failed: no credentials provided");

				throw new BadCredentialsException(messages.getMessage(
						"AbstractUserDetailsAuthenticationProvider.badCredentials","Bad credentials"));
			}
			//下面是真正的验证逻辑
			//应该在service里面封装一下。在这里调用。
			authentication.getCredentials().toString();//原生未加密密码
			authentication.getName();//这里不对，这是真正的密码，不是动态密码，应该获取用户名，dao里面根据用户名得到code
			if(usersMapper.getDynamicCodeByPhonenumber(authentication.getName())!=Integer.parseInt(authentication.getCredentials().toString())){
				logger.debug("Authentication failed: password does not match stored value");

				throw new BadCredentialsException(messages.getMessage(
						"AbstractUserDetailsAuthenticationProvider.badCredentials",
						"Bad credentials"));
			}
//			if (!passwordEncoder.isPasswordValid(userDetails.getPassword(),
//					presentedPassword, salt)) {
//				logger.debug("Authentication failed: password does not match stored value");
//
//				throw new BadCredentialsException(messages.getMessage(
//						"AbstractUserDetailsAuthenticationProvider.badCredentials",
//						"Bad credentials"));
//			}
			//additionalAuthenticationChecks方法完毕
		}
		catch (AuthenticationException exception) {
			if (cacheWasUsed) {
				// There was a problem, so try again after checking
				// we're using latest data (i.e. not from the cache)
				cacheWasUsed = false;
//				user = retrieveUser(username,
//						(UsernamePasswordAuthenticationToken) authentication);
				//下面是retrieveUser方法的代替
				UserDetails loadedUser;

				try {
					//这里要改，userdetailservice要加一个根据手机号返回的。
					loadedUser = this.getUserDetailsService().loadUserByUsername(username);
					//这 里要改
				}
				catch (UsernameNotFoundException notFound) {
					//没有这个手机号
					if (authentication.getCredentials() != null) {
						String presentedPassword = authentication.getCredentials().toString();
						
					}
					throw notFound;
				}
				catch (Exception repositoryProblem) {
					throw new InternalAuthenticationServiceException(
							repositoryProblem.getMessage(), repositoryProblem);
				}

				if (loadedUser == null) {
					throw new InternalAuthenticationServiceException(
							"UserDetailsService returned null, which is an interface contract violation");
				}
				user=loadedUser;
				//上面是retrieveUser方法的代替
				getPreAuthenticationChecks().check(user);
				//下面一句跟进去，看94行，在比对密码，所以这里最好是实现一下，改了改成验证code，或者直接把逻辑写在下面。
				//userdetails里面的getpassword得到的是数据库里面的。大概知道该重写哪了。retrieveUser和下面这个函数
				//retrieveUser方法会从数据库里拿出user的信息。additionalAuthenticationChecks会验证一下是不是一样
				//验证的问题就解决了，剩下的就是怎么让系统知道phonenumber代表的那个username验证成功了。。
				additionalAuthenticationChecks(user,
						(UsernamePasswordAuthenticationToken) authentication);
				//下面是additionalAuthenticationchecks的实现
				if (authentication.getCredentials() == null) {
					logger.debug("Authentication failed: no credentials provided");
					throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials","Bad credentials"));
				}
				//下面是真正的验证逻辑
				if(usersMapper.getDynamicCodeByPhonenumber(authentication.getName())!=Integer.parseInt(authentication.getCredentials().toString())){
					logger.debug("Authentication failed: password does not match stored value");

					throw new BadCredentialsException(messages.getMessage(
							"AbstractUserDetailsAuthenticationProvider.badCredentials",
							"Bad credentials"));
				}
				//结束
			}
			else {
				throw exception;
			}
		}
		//验证过期的。
		getPostAuthenticationChecks().check(user);

		if (!cacheWasUsed) {
			this.getUserCache().putUserInCache(user);
		}

		Object principalToReturn = user;

		if (isForcePrincipalAsString()) {
			principalToReturn = user.getUsername();
		}

		return createSuccessAuthentication(principalToReturn, authentication, user);
	}

}
